#include <algorithm>
#include <sys/uio.h>

#include "Buffer.h"
#include "StringPiece.h"

Buffer::Buffer(size_t initialSize):
    buffer_(initialSize + kCheapPrepend),
    readerIdx_(kCheapPrepend),
    writerIdx_(kCheapPrepend)
{

}

int Buffer::readFd(int fd, int* savedErrno) {
    char extrabuf[65536];
    iovec vec[2];
    const size_t writeable = writeableBytes();
    vec[0].iov_base = buffer_.data() + writerIdx_;
    vec[0].iov_len = writeable;
    vec[1].iov_base = extrabuf;
    vec[1].iov_len = 65536;
    const ssize_t n = readv(fd, vec, 2);
    if (n < 0) {
        *savedErrno = errno;
    } else if (n <= writeable) {
        writerIdx_ += n;
    } else {
        writerIdx_ = buffer_.size();
        append(extrabuf, n - writeable);
    }
    return n;
}

size_t Buffer::prependableBytes() {
    return readerIdx_;
}

size_t Buffer::readableBytes() {
    return writerIdx_ - readerIdx_;
}

const char* Buffer::peek() {
    return buffer_.data() + readerIdx_;
}

size_t Buffer::retrive(size_t len) {
    if (len > readableBytes()) {
        len = readableBytes();
    }
    if (len < readableBytes()) {
        readerIdx_ += len;
    } else {
        readerIdx_ = writerIdx_ = kCheapPrepend;
    }

    return len;
}

void Buffer::append(const char* str, size_t len) {
    if (len > writeableBytes()) {
        makeSpace(len);
    }    
    std::copy(str,
              str + len,
              buffer_.begin() + writerIdx_);
    writerIdx_ += len;
}

void Buffer::append(const StringPiece& str) {
    append(str.data(), str.size());
}


string Buffer::toString() {
    return string(peek(), readableBytes());
}


size_t Buffer::writeableBytes() {
    return buffer_.size() - writerIdx_;
}

void Buffer::makeSpace(size_t len) {
    std::copy(buffer_.begin() + readerIdx_, 
                buffer_.begin() + writerIdx_, 
                buffer_.begin() + kCheapPrepend);    
    if (prependableBytes() + writeableBytes() - kCheapPrepend >= len) {
        writerIdx_ -= (readerIdx_ - kCheapPrepend);
        readerIdx_ = kCheapPrepend;
    } else {
        buffer_.resize(writerIdx_ + len);
    }
}

